//===============================================================================
// Microsoft Architecture Strategy Team
// LitwareHR - SaaS Sample Application
//===============================================================================
// Copyright  Microsoft Corporation.  All rights reserved.
// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY
// OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT
// LIMITED TO THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
// FITNESS FOR A PARTICULAR PURPOSE.
//===============================================================================
// The example companies, organizations, products, domain names,
// e-mail addresses, logos, people, places, and events depicted
// herein are fictitious.  No association with any real company,
// organization, product, domain name, email address, logo, person,
// places, or events is intended or should be inferred.
//===============================================================================


namespace Shp.Runtime.OfflineSitka
{
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;

    public class TokenProvider
    {
        private int index;
        private string source;
        private bool readNext;
        private char lastChar;

        public TokenProvider(string source)
        {
            readNext = true;
            this.source = source;
        }

        public Token Next
        {
            get
            {
                return ParseNextTocken();
            }
        }
        
        private char NextChar()
        {
            if (!readNext)
            {
                readNext = true;
                return lastChar;
            }
            
            if (index < source.Length)
            {
                lastChar = source[index];
                index++;
            }
            else
            {
                lastChar = char.MinValue;
            }

            return lastChar;
        }

        private char GetNoSpaceCharacter()
        {
            char waitChar = NextChar();
            while (waitChar == ' ')
            {
                waitChar = NextChar();
            }

            return waitChar;
        }

        private void WaitCharacter(char character)
        {
            char waitChar = GetNoSpaceCharacter();
            if (waitChar != character)
            {
                throw new InvalidCharacterException();
            }
        }

        private string GetStringValue()
        {            
            string value = string.Empty;
            char charValue = NextChar();
            while (charValue != '"') 
            {
                value += charValue;
                charValue = NextChar();
            }

            return value;
        }

        private string ParseSimpleString()
        {
            string value = string.Empty;
            char charValue = NextChar();
            while (char.IsLetter(charValue))
            {
                value += charValue;
                charValue = NextChar();
            }

            readNext = false;
            return value;
        }

        private Token ParsePropertyTocken()
        {
            Token token = new Token();
            char nextCharacter = NextChar();
            if (nextCharacter == '.')
            {                
                token.TokenType = TokenType.IntrinsicProperty;
                token.Value = ParseSimpleString();
                return token;
            }
            else if (nextCharacter == '[')
            {
                token.TokenType = TokenType.CustomProperty;
                WaitCharacter('"');
                token.Value = GetStringValue();
                WaitCharacter(']');
                return token;
            }

            throw new InvalidCharacterException();
        }

        private Token ParseStringValueTocken()
        {
            return new Token(TokenType.StringValue, GetStringValue());
        }

        private Token ParseAndTocken()
        {            
            if (NextChar() == '&')
            {
                return new Token(TokenType.Operator, "&&");       
            }

            throw new InvalidCharacterException();
        }

        private Token ParseOrTocken()
        {
            if (NextChar() == '|')
            {
                return new Token(TokenType.Operator, "||");
            }

            throw new InvalidCharacterException();
        }

        private Token WaitEqualCharacter(string operandValue)
        {
            if (NextChar() == '=')
            {
                return new Token(TokenType.Operator, operandValue);
            }
            else
            {
                this.readNext = false;
                return null;
            }            
        }

        private Token ParseDateTimeValue()
        {
            Token token = null;
            readNext = false;
            string identifier = ParseSimpleString();
            if (identifier.ToUpperInvariant() == "DATETIME")
            {
                WaitCharacter('(');
                WaitCharacter('"');
                string dateValue = GetStringValue();                
                WaitCharacter(')');

                token = new Token(TokenType.DateValue, dateValue);
            }

            return token;
        }

        private Token ParseEqualToken()
        {
            Token token = WaitEqualCharacter("==");
            if (token == null)
            {
                throw new InvalidCharacterException();
            }
            return token;
        }

        private Token ParseNotEqualToken()
        {
            Token token = WaitEqualCharacter("!=");
            if (token == null)
            {
                throw new InvalidCharacterException();
            }
            return token;
        }

        private Token ParseNextTocken()
        {
            Token token = null;
            char character = GetNoSpaceCharacter();
            switch (character)
            {
                case 'e':
                case 'E':
                    token = ParsePropertyTocken();
                    break;
                case '"':
                    token = ParseStringValueTocken();
                    break;
                case '&':
                    token = ParseAndTocken();
                    break;
                case '=':
                    token = ParseEqualToken();
                    break;
                case '!':
                    token = ParseNotEqualToken();
                    break;
                case '|':
                    token = ParseOrTocken();
                    break;
                case '<':                    
                case '>':
                    token = WaitEqualCharacter(character+"=");
                    if (token == null)
                    {
                        token = new Token(TokenType.Operator, character.ToString());
                    }
                    break;
                case '(':
                    token = new Token(TokenType.Symbol, "(");
                    break;
                case ')':
                    token = new Token(TokenType.Symbol, ")");
                    break;                
                case 't':
                case 'T':
                case 'f':
                case 'F':
                    readNext = false;
                    token = new Token(TokenType.BoolValue, ParseSimpleString());
                    break;
                case 'd':
                case 'D':
                    token = ParseDateTimeValue();
                    break;
                case char.MinValue:
                    token = null;
                    break;                
                default:
                    throw new InvalidCharacterException();
            }

            return token;
        }        
    }
}
